﻿// This class scans Diamond source code into tokens.



using Collections = System.Collections.Generic;
using System.Collections.Generic;
using IO = System.IO;
using Text = System.Text;
public class StringLiteral
{
    public string Value;
}
public class Keyword
{
    public string Value;
}
public class Scanner
{
    private readonly Collections.IList<object> result;

    public Scanner(IO.TextReader input)
    {
        this.result = new Collections.List<object>();
        this.Scan(input);
    }
    public Collections.IList<object> Tokens
    {
        get { return this.result; }
    }
    public static readonly object Add = new object();
    public static readonly object Sub = new object();
    public static readonly object Mul = new object();
    public static readonly object Div = new object();
    public static readonly object Semi = new object();
    public static readonly object Assign = new object();
    public static readonly object OpenBracket = new object();
    public static readonly object CloseBracker = new object();
    public static readonly object OpenPar = new object();
    public static readonly object ClosePar = new object();
    public static readonly object Comma = new object();
    public static readonly object EOL = new object();
    public static readonly object Dot = new object();
    public static readonly object And = new object();
    public static readonly object Equal = new object();
    public static readonly object NotEqual = new object();
    public static readonly object Colon = new object();
    public static readonly object At = new object();
    public static readonly object GreaterThan = new object();
    public static readonly object LessThen = new object();
    public static readonly object PlusEqual = new object();
    public static List<List<int>> Lines = new List<List<int>>();
    int line = 0;
    private void Scan(IO.TextReader input)
    {
        Lines = new List<List<int>>();
        Lines.Add(new List<int>());
        while (input.Peek() != -1)
        {
            char ch = (char)input.Peek();
            Lines[line].Add(result.Count);
            // Comment , #
            if (ch == '#')
            {
                while (ch != '\n')
                {
                    ch = (char)input.Read();
                }
            }
            else if (ch == '\n')
            {
                input.Read();
                Lines.Add(new List<int>());
                line++;

            }
            else if (char.IsWhiteSpace(ch))
            {
                input.Read();
            }

            else if (char.IsLetter(ch) || ch == '_')
            {
                Text.StringBuilder accum = new Text.StringBuilder();

                while (char.IsLetter(ch) || char.IsNumber(ch))
                {
                    accum.Append(ch);
                    input.Read();

                    if (input.Peek() == -1)
                    {
                        break;
                    }
                    else
                    {
                        ch = (char)input.Peek();
                    }
                }
                Keyword k = new Keyword();
                k.Value = accum.ToString();
                this.result.Add(k.Value);
            }
            else if (ch == '\'')
            {
                input.Read();
                result.Add((char)input.Read());
                if (((char)input.Read()) != '\'')
                {
                    throw new System.Exception("Expected '");
                }
                
            }
            else if (ch == '"')
            {
                Text.StringBuilder accum = new Text.StringBuilder();

                input.Read(); // skip the '"'

                if (input.Peek() == -1)
                {
                    throw new System.Exception("Expected \"");
                }

                while ((ch = (char)input.Peek()) != '"')
                {
                    accum.Append(ch);
                    input.Read();

                    if (input.Peek() == -1)
                    {
                        throw new System.Exception("Expected \"");
                    }
                }
                StringLiteral sl = new StringLiteral();
                input.Read();
                sl.Value = accum.ToString();
                this.result.Add(sl);
            }
            else if (char.IsDigit(ch))
            {
                // numeric literal

                Text.StringBuilder accum = new Text.StringBuilder();

                while (char.IsDigit(ch))
                {
                    accum.Append(ch);
                    input.Read();

                    if (input.Peek() == -1)
                    {
                        break;
                    }
                    else
                    {
                        ch = (char)input.Peek();
                    }
                }

                this.result.Add(int.Parse(accum.ToString()));
            }
            else switch (ch)
                {
                    case '+':
                        input.Read();
                        if (input.Peek() == (int)'=')
                        {
                            input.Read();
                            this.result.Add(Scanner.PlusEqual);
                            break;
                        }
                        this.result.Add(Scanner.Add);
                        break;

                    case '-':
                        input.Read();
                        this.result.Add(Scanner.Sub);
                        break;

                    case '*':
                        input.Read();
                        this.result.Add(Scanner.Mul);
                        break;
                    case '@':
                        input.Read();
                        this.result.Add(Scanner.At);
                        break;
                    case '/':
                        input.Read();
                        this.result.Add(Scanner.Div);
                        break;
                    case '!':
                        input.Read();
                        if ((char)(input.Peek()) == '=')
                        {
                            input.Read();
                            this.result.Add(Scanner.NotEqual);
                        }
                        break;
                    case '=':
                        input.Read();
                        if (((char)input.Peek()) == '=')
                        {
                            input.Read();
                            this.result.Add(Scanner.Equal);
                        }
                        else
                            this.result.Add(Scanner.Assign);
                        break;

                    case ';':
                        input.Read();
                        this.result.Add(Scanner.Semi);
                        break;
                    case '[':
                        input.Read();
                        this.result.Add(Scanner.OpenBracket);
                        break;
                    case ']':
                        input.Read();
                        this.result.Add(Scanner.CloseBracker);
                        break;
                    case '(':
                        input.Read();
                        this.result.Add(Scanner.OpenPar);
                        break;
                    case '<':
                        input.Read();
                        this.result.Add(Scanner.LessThen);
                        break;
                    case '>':
                        input.Read();
                        this.result.Add(Scanner.GreaterThan);
                        break;
                    case ')':
                        input.Read();
                        this.result.Add(Scanner.ClosePar);
                        break;
                    case ',':
                        input.Read();
                        this.result.Add(Scanner.Comma);
                        break;
                    case '.':
                        input.Read();
                        this.result.Add(Scanner.Dot);
                        break;
                    case '&':
                        input.Read();
                        this.result.Add(Scanner.And);
                        break;
                    case ':':
                        input.Read();
                        this.result.Add(Scanner.Colon);
                        break;
                    default:
                        throw new System.Exception("Scanner encountered unrecognized character '" + ch + "'");
                }

        }
    }
}
